package com.sz.admin.system.service.impl;

import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.sz.admin.dtuinfo.pojo.po.DtuInfo;
import com.sz.admin.dtuinfo.service.DtuInfoService;
import com.sz.admin.system.mapper.SysMonitorFileRecordMapper;
import com.sz.admin.system.pojo.po.SysMonitorFileRecord;
import com.sz.admin.system.pojo.vo.filemonitor.SysMonitorFileRecordVO;
import com.sz.admin.system.service.SysMonitorFileRecordService;
import com.sz.admin.system.service.SysDeptClosureService;
import com.sz.admin.vehicleinfo.pojo.po.VehicleInfo;
import com.sz.admin.vehicleinfo.service.VehicleInfoService;
import com.sz.admin.vehicleschedule.pojo.po.VehicleSchedule;
import com.sz.admin.vehicleschedule.service.VehicleScheduleService;
import com.sz.core.util.BeanCopyUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.time.LocalTime;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
import java.util.ArrayList;

import static com.mybatisflex.core.query.QueryMethods.*;
import static com.sz.admin.dtuinfo.pojo.po.table.DtuInfoTableDef.DTU_INFO;

/**
 * 监控文件记录服务实现类
 *
 * @author sz-admin
 * @since 2025-01-13
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SysMonitorFileRecordServiceImpl extends ServiceImpl<SysMonitorFileRecordMapper, SysMonitorFileRecord> implements SysMonitorFileRecordService {

    private final DtuInfoService dtuInfoService;
    private final VehicleInfoService vehicleInfoService;
    private final VehicleScheduleService vehicleScheduleService;
    private final SysDeptClosureService deptClosureService;

    @Override
    public void recordFileProcess(Long monitorId, File originalFile, String newFilename, 
                                 String storagePath, String objectName, String fileUrl, 
                                 boolean success, String errorMessage) {
        // 调用重载方法，MD5设为 null，让方法自行计算
        recordFileProcess(monitorId, originalFile, newFilename, storagePath, objectName, 
                         fileUrl, success, errorMessage, null);
    }
    
    @Override
    public void recordFileProcess(Long monitorId, File originalFile, String newFilename, 
                                 String storagePath, String objectName, String fileUrl, 
                                 boolean success, String errorMessage, String precomputedMd5) {
        SysMonitorFileRecord record = new SysMonitorFileRecord();
        record.setMonitorId(monitorId);
        record.setOriginalPath(originalFile.getAbsolutePath());
        record.setOriginalFilename(originalFile.getName());
        record.setNewFilename(newFilename);
        record.setStoragePath(storagePath);
        record.setFileSize(originalFile.length());
        record.setObjectName(objectName);
        record.setFileUrl(fileUrl);
        record.setProcessStatus(success ? "1" : "0");
        record.setErrorMessage(errorMessage);
        record.setProcessTime(LocalDateTime.now());
        record.setCreateTime(LocalDateTime.now());
        record.setDelFlag("F");
        
        // 获取文件类型
        String filename = originalFile.getName();
        int lastDotIndex = filename.lastIndexOf('.');
        if (lastDotIndex > 0) {
            record.setFileType(filename.substring(lastDotIndex + 1).toLowerCase());
        }
        
        // 设置MD5：优先使用预计算的MD5，否则重新计算
        if (precomputedMd5 != null && !precomputedMd5.trim().isEmpty()) {
            record.setFileMd5(precomputedMd5);
            log.debug("使用预计算的MD5: {}", precomputedMd5);
        } else {
            // 计算文件MD5
            try {
                String calculatedMd5 = calculateFileMD5(originalFile);
                record.setFileMd5(calculatedMd5);
                log.debug("重新计算文件MD5: {}", calculatedMd5);
            } catch (IOException e) {
                log.warn("计算文件MD5失败: {}", e.getMessage());
            }
        }
        
        save(record);
    }
    
    @Override
    public void recordFileProcessWithTag(Long monitorId, File originalFile, String newFilename, 
                                        String storagePath, String objectName, String fileUrl, 
                                        boolean success, String errorMessage, String precomputedMd5,
                                        String fileTag, Long sfId) {
        
        SysMonitorFileRecord record = new SysMonitorFileRecord();
        record.setMonitorId(monitorId);
        record.setOriginalPath(originalFile.getAbsolutePath());
        record.setOriginalFilename(originalFile.getName());
        record.setNewFilename(newFilename);
        record.setStoragePath(storagePath);
        record.setFileSize(originalFile.length());
        record.setFileType(getFileExtension(originalFile.getName()));
        record.setObjectName(objectName);
        record.setFileUrl(fileUrl);
        record.setProcessStatus(success ? "1" : "0");
        record.setErrorMessage(errorMessage);
        record.setProcessTime(LocalDateTime.now());
        record.setCreateTime(LocalDateTime.now());
        record.setDelFlag("F");
        
        // 设置新字段
        record.setFileTag(fileTag);
        record.setSfId(sfId);
        
        // 设置司机ID - 使用新的逻辑
        Long driverId = getCurrentDriverIdByFileTagAndTime(fileTag, record.getCreateTime());
        record.setDriverId(driverId);
        
        // 设置MD5
        if (precomputedMd5 != null) {
            record.setFileMd5(precomputedMd5);
        } else {
            try {
                record.setFileMd5(calculateFileMD5(originalFile));
            } catch (IOException e) {
                log.error("计算文件MD5失败: {}", e.getMessage(), e);
            }
        }
        
        save(record);
    }

    @Override
    public void recordFileProcessWithOCR(Long monitorId, File originalFile, String newFilename, 
                                         String storagePath, String objectName, String fileUrl, 
                                         boolean success, String errorMessage, String precomputedMd5,
                                         String fileTag, Long sfId, String carNumber, String carNumberRes) {
        
        SysMonitorFileRecord record = new SysMonitorFileRecord();
        record.setMonitorId(monitorId);
        record.setOriginalPath(originalFile.getAbsolutePath());
        record.setOriginalFilename(originalFile.getName());
        record.setNewFilename(newFilename);
        record.setStoragePath(storagePath);
        record.setFileSize(originalFile.length());
        record.setFileType(getFileExtension(originalFile.getName()));
        record.setObjectName(objectName);
        record.setFileUrl(fileUrl);
        record.setProcessStatus(success ? "1" : "0");
        record.setErrorMessage(errorMessage);
        record.setProcessTime(LocalDateTime.now());
        record.setCreateTime(LocalDateTime.now());
        record.setDelFlag("F");
        
        // 设置原有字段
        record.setFileTag(fileTag);
        record.setSfId(sfId);
        
        // 设置OCR识别结果
        record.setCarNumber(carNumber);
        record.setCarNumberRes(carNumberRes);
        
        // 设置司机ID和车辆ID - 使用新的逻辑
        DriverVehicleInfo driverVehicleInfo = getDriverAndVehicleIdByCarNumberAndTime(carNumber, record.getCreateTime());
        record.setDriverId(driverVehicleInfo.getDriverId());
        record.setCarId(driverVehicleInfo.getVehicleId());
        
        // 设置MD5
        if (precomputedMd5 != null) {
            record.setFileMd5(precomputedMd5);
        } else {
            try {
                record.setFileMd5(calculateFileMD5(originalFile));
            } catch (IOException e) {
                log.error("计算文件MD5失败: {}", e.getMessage(), e);
            }
        }
        
        save(record);
    }

    @Override
    public List<SysMonitorFileRecordVO> getRecordsByMonitorId(Long monitorId) {
        QueryWrapper wrapper = QueryWrapper.create()
                .eq(SysMonitorFileRecord::getMonitorId, monitorId)
                .orderBy(SysMonitorFileRecord::getProcessTime, false);
        
        List<SysMonitorFileRecord> records = list(wrapper);
        return BeanCopyUtils.copyList(records, SysMonitorFileRecordVO.class);
    }

    @Override
    public void updateMonitorStatistics(Long monitorId) {
        // 这个方法可以用来更新监控配置的统计信息
        // 由于统计信息在VO中，这里可以根据需要实现
        log.info("更新监控统计信息: monitorId={}", monitorId);
    }

    private String calculateFileMD5(File file) throws IOException {
        try (FileInputStream fis = new FileInputStream(file)) {
            return DigestUtils.md5DigestAsHex(fis);
        }
    }

    private String getFileExtension(String filename) {
        if (filename == null || filename.isEmpty()) {
            return "";
        }
        int lastDotIndex = filename.lastIndexOf('.');
        return lastDotIndex > 0 ? filename.substring(lastDotIndex + 1).toLowerCase() : "";
    }
    
    /**
     * 根据设备编号获取当前司机ID
     * @param fileTag 设备编号
     * @return 当前司机ID，如果无法确定则返回null
     */
    private Long getCurrentDriverId(String fileTag) {
        try {
            // 1. 根据设备编号(fileTag)查找DTU信息
            QueryWrapper dtuQuery = QueryWrapper.create()
                    .select()
                    .from(DtuInfo.class)
                    .where(DTU_INFO.DTU_NUMBER.eq(fileTag));
            
            List<DtuInfo> dtuInfos = dtuInfoService.listAs(dtuQuery, DtuInfo.class);
            if (dtuInfos == null || dtuInfos.isEmpty()) {
                log.warn("未找到设备编号为 {} 的DTU信息", fileTag);
                return null;
            }
            
            DtuInfo dtuInfo = dtuInfos.get(0);
            Long vehicleId = dtuInfo.getVehicleId();
            if (vehicleId == null) {
                log.warn("设备编号为 {} 的DTU未绑定车辆", fileTag);
                return null;
            }
            
            // 2. 根据车辆ID查找车辆信息
            VehicleInfo vehicleInfo = vehicleInfoService.getById(vehicleId);
            if (vehicleInfo == null) {
                log.warn("未找到ID为 {} 的车辆信息", vehicleId);
                return null;
            }
            
            Long scheduleId = vehicleInfo.getScheduleId();
            if (scheduleId == null) {
                log.warn("车辆ID为 {} 的车辆未设置排班", vehicleId);
                return null;
            }
            
            // 3. 根据排班ID查找排班信息
            VehicleSchedule schedule = vehicleScheduleService.getById(scheduleId);
            if (schedule == null) {
                log.warn("未找到ID为 {} 的排班信息", scheduleId);
                return null;
            }
            
            // 4. 根据当前时间和排班信息确定当前司机
            LocalTime currentTime = LocalTime.now();
            String scheduleType = schedule.getScheduleType();
            LocalTime time1 = schedule.getTime1();
            LocalTime time2 = schedule.getTime2();
            LocalTime time3 = schedule.getTime3();
            
            Long userId1 = vehicleInfo.getUserId1();
            Long userId2 = vehicleInfo.getUserId2();
            Long userId3 = vehicleInfo.getUserId3();
            
            // 根据排班类型和当前时间确定当前司机
            if ("2001002".equals(scheduleType)) {
                // 两班制
                if (time1 != null && time2 != null) {
                    if (isTimeInPeriod(currentTime, time1, time2)) {
                        return userId1;
                    } else {
                        return userId2;
                    }
                }
            } else if ("2001003".equals(scheduleType)) {
                // 三班制
                if (time1 != null && time2 != null && time3 != null) {
                    if (isTimeInPeriod(currentTime, time1, time2)) {
                        return userId1;
                    } else if (isTimeInPeriod(currentTime, time2, time3)) {
                        return userId2;
                    } else {
                        return userId3;
                    }
                }
            }
            
            // 默认返回第一个司机
            return userId1;
        } catch (Exception e) {
            log.error("获取设备编号为 {} 的当前司机ID时发生错误: {}", fileTag, e.getMessage(), e);
            return null;
        }
    }
    
    /**
     * 根据设备编号和创建时间获取对应的司机ID
     * @param fileTag 设备编号
     * @param createTime 创建时间
     * @return 对应的司机ID
     */
    private Long getCurrentDriverIdByFileTagAndTime(String fileTag, LocalDateTime createTime) {
        try {
            // 1. 根据设备编号(fileTag)查找DTU信息
            QueryWrapper dtuQuery = QueryWrapper.create()
                    .select()
                    .from(DtuInfo.class)
                    .where(DTU_INFO.DTU_NUMBER.eq(fileTag));
            
            List<DtuInfo> dtuInfos = dtuInfoService.listAs(dtuQuery, DtuInfo.class);
            if (dtuInfos == null || dtuInfos.isEmpty()) {
                log.warn("未找到设备编号为 {} 的DTU信息", fileTag);
                return null;
            }
            
            DtuInfo dtuInfo = dtuInfos.get(0);
            Long vehicleId = dtuInfo.getVehicleId();
            if (vehicleId == null) {
                log.warn("设备编号为 {} 的DTU未绑定车辆", fileTag);
                return null;
            }
            
            // 2. 根据车辆ID查找车辆信息
            VehicleInfo vehicleInfo = vehicleInfoService.getById(vehicleId);
            if (vehicleInfo == null) {
                log.warn("未找到ID为 {} 的车辆信息", vehicleId);
                return null;
            }
            
            Long scheduleId = vehicleInfo.getScheduleId();
            if (scheduleId == null) {
                log.warn("车辆ID为 {} 的车辆未设置排班", vehicleId);
                return null;
            }
            
            // 3. 根据排班ID查找排班信息
            VehicleSchedule schedule = vehicleScheduleService.getById(scheduleId);
            if (schedule == null) {
                log.warn("未找到ID为 {} 的排班信息", scheduleId);
                return null;
            }
            
            // 4. 根据创建时间和排班信息确定对应司机
            LocalTime recordTime = createTime.toLocalTime();
            String scheduleType = schedule.getScheduleType();
            LocalTime time1 = schedule.getTime1();
            LocalTime time2 = schedule.getTime2();
            LocalTime time3 = schedule.getTime3();
            
            Long userId1 = vehicleInfo.getUserId1();
            Long userId2 = vehicleInfo.getUserId2();
            Long userId3 = vehicleInfo.getUserId3();
            
            // 根据排班类型和记录时间确定对应司机
            if ("2001002".equals(scheduleType)) {
                // 两班制
                if (time1 != null && time2 != null) {
                    if (isTimeInPeriod(recordTime, time1, time2)) {
                        return userId1;
                    } else {
                        return userId2;
                    }
                }
            } else if ("2001003".equals(scheduleType)) {
                // 三班制
                if (time1 != null && time2 != null && time3 != null) {
                    if (isTimeInPeriod(recordTime, time1, time2)) {
                        return userId1;
                    } else if (isTimeInPeriod(recordTime, time2, time3)) {
                        return userId2;
                    } else {
                        return userId3;
                    }
                }
            }
            
            // 默认返回第一个司机
            return userId1;
        } catch (Exception e) {
            log.error("根据设备编号 {} 和时间 {} 获取司机ID时发生错误: {}", fileTag, createTime, e.getMessage(), e);
            return null;
        }
    }
    
    /**
     * 根据车牌号和创建时间获取对应的司机ID
     * @param carNumber 车牌号
     * @param createTime 创建时间
     * @return 对应的司机ID
     */
    private Long getCurrentDriverIdByCarNumberAndTime(String carNumber, LocalDateTime createTime) {
        try {
            // 1. 根据车牌号查找车辆信息
            QueryWrapper vehicleQuery = QueryWrapper.create()
                    .select()
                    .from(VehicleInfo.class)
                    .where(VehicleInfo::getVehicleNumber).eq(carNumber);
            
            List<VehicleInfo> vehicleInfos = vehicleInfoService.listAs(vehicleQuery, VehicleInfo.class);
            if (vehicleInfos == null || vehicleInfos.isEmpty()) {
                log.warn("未找到车牌号为 {} 的车辆信息", carNumber);
                return null;
            }
            
            VehicleInfo vehicleInfo = vehicleInfos.get(0);
            Long scheduleId = vehicleInfo.getScheduleId();
            if (scheduleId == null) {
                log.warn("车牌号为 {} 的车辆未设置排班", carNumber);
                return null;
            }
            
            // 2. 根据排班ID查找排班信息
            VehicleSchedule schedule = vehicleScheduleService.getById(scheduleId);
            if (schedule == null) {
                log.warn("未找到ID为 {} 的排班信息", scheduleId);
                return null;
            }
            
            // 3. 根据创建时间和排班信息确定对应司机
            LocalTime recordTime = createTime.toLocalTime();
            String scheduleType = schedule.getScheduleType();
            LocalTime time1 = schedule.getTime1();
            LocalTime time2 = schedule.getTime2();
            LocalTime time3 = schedule.getTime3();
            
            Long userId1 = vehicleInfo.getUserId1();
            Long userId2 = vehicleInfo.getUserId2();
            Long userId3 = vehicleInfo.getUserId3();
            
            // 根据排班类型和记录时间确定对应司机
            if ("2001002".equals(scheduleType)) {
                // 两班制
                if (time1 != null && time2 != null) {
                    if (isTimeInPeriod(recordTime, time1, time2)) {
                        return userId1;
                    } else {
                        return userId2;
                    }
                }
            } else if ("2001003".equals(scheduleType)) {
                // 三班制
                if (time1 != null && time2 != null && time3 != null) {
                    if (isTimeInPeriod(recordTime, time1, time2)) {
                        return userId1;
                    } else if (isTimeInPeriod(recordTime, time2, time3)) {
                        return userId2;
                    } else {
                        return userId3;
                    }
                }
            }
            
            // 默认返回第一个司机
            return userId1;
        } catch (Exception e) {
            log.error("根据车牌号 {} 和时间 {} 获取司机ID时发生错误: {}", carNumber, createTime, e.getMessage(), e);
            return null;
        }
    }
    
    /**
     * 根据车牌号和创建时间获取对应的司机ID和车辆ID
     * @param carNumber 车牌号
     * @param createTime 创建时间
     * @return 对应的司机和车辆信息
     */
    private DriverVehicleInfo getDriverAndVehicleIdByCarNumberAndTime(String carNumber, LocalDateTime createTime) {
        DriverVehicleInfo result = new DriverVehicleInfo();
        try {
            // 1. 根据车牌号查找车辆信息
            QueryWrapper vehicleQuery = QueryWrapper.create()
                    .select()
                    .from(VehicleInfo.class)
                    .where(VehicleInfo::getVehicleNumber).eq(carNumber);
            
            List<VehicleInfo> vehicleInfos = vehicleInfoService.listAs(vehicleQuery, VehicleInfo.class);
            if (vehicleInfos == null || vehicleInfos.isEmpty()) {
                log.warn("未找到车牌号为 {} 的车辆信息", carNumber);
                return result;
            }
            
            VehicleInfo vehicleInfo = vehicleInfos.get(0);
            result.setVehicleId(vehicleInfo.getId()); // 设置车辆ID
            
            Long scheduleId = vehicleInfo.getScheduleId();
            if (scheduleId == null) {
                log.warn("车牌号为 {} 的车辆未设置排班", carNumber);
                return result;
            }
            
            // 2. 根据排班ID查找排班信息
            VehicleSchedule schedule = vehicleScheduleService.getById(scheduleId);
            if (schedule == null) {
                log.warn("未找到ID为 {} 的排班信息", scheduleId);
                return result;
            }
            
            // 3. 根据创建时间和排班信息确定对应司机
            LocalTime recordTime = createTime.toLocalTime();
            String scheduleType = schedule.getScheduleType();
            LocalTime time1 = schedule.getTime1();
            LocalTime time2 = schedule.getTime2();
            LocalTime time3 = schedule.getTime3();
            
            Long userId1 = vehicleInfo.getUserId1();
            Long userId2 = vehicleInfo.getUserId2();
            Long userId3 = vehicleInfo.getUserId3();
            
            // 根据排班类型和记录时间确定对应司机
            if ("2001002".equals(scheduleType)) {
                // 两班制
                if (time1 != null && time2 != null) {
                    if (isTimeInPeriod(recordTime, time1, time2)) {
                        result.setDriverId(userId1);
                    } else {
                        result.setDriverId(userId2);
                    }
                }
            } else if ("2001003".equals(scheduleType)) {
                // 三班制
                if (time1 != null && time2 != null && time3 != null) {
                    if (isTimeInPeriod(recordTime, time1, time2)) {
                        result.setDriverId(userId1);
                    } else if (isTimeInPeriod(recordTime, time2, time3)) {
                        result.setDriverId(userId2);
                    } else {
                        result.setDriverId(userId3);
                    }
                }
            } else {
                // 默认返回第一个司机
                result.setDriverId(userId1);
            }
            
            return result;
        } catch (Exception e) {
            log.error("根据车牌号 {} 和时间 {} 获取司机和车辆ID时发生错误: {}", carNumber, createTime, e.getMessage(), e);
            return result;
        }
    }

    /**
     * 判断当前时间是否在指定时间段内
     * @param currentTime 当前时间
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @return 是否在时间段内
     */
    private boolean isTimeInPeriod(LocalTime currentTime, LocalTime startTime, LocalTime endTime) {
        if (endTime.isAfter(startTime)) {
            // 同一天的情况
            return !currentTime.isBefore(startTime) && currentTime.isBefore(endTime);
        } else {
            // 跨天的情况
            return !currentTime.isBefore(startTime) || currentTime.isBefore(endTime);
        }
    }

}